<!--
 - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
 - SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
	<NcHeaderMenu
		id="user-menu"
		class="account-menu"
		is-nav
		:aria-label="t('core', 'Settings menu')"
		:description="avatarDescription">
		<template #trigger>
			<!-- The `key` is a hack as NcAvatar does not handle updating the preloaded status on show status change -->
			<NcAvatar
				:key="String(showUserStatus)"
				class="account-menu__avatar"
				disable-menu
				disable-tooltip
				:hide-user-status="!showUserStatus"
				:user="currentUserId"
				:preloaded-user-status="userStatus" />
		</template>
		<ul class="account-menu__list">
			<AccountMenuProfileEntry
				:id="profileEntry.id"
				:name="profileEntry.name"
				:href="profileEntry.href"
				:active="profileEntry.active" />
			<AccountMenuEntry
				v-for="entry in otherEntries"
				:id="entry.id"
				:key="entry.id"
				:name="entry.name"
				:href="entry.href"
				:active="entry.active"
				:icon="entry.icon" />
		</ul>
	</NcHeaderMenu>
</template>

<script lang="ts">
import { getCurrentUser } from '@nextcloud/auth'
import axios from '@nextcloud/axios'
import { getCapabilities } from '@nextcloud/capabilities'
import { emit, subscribe } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import { generateOcsUrl } from '@nextcloud/router'
import { defineComponent } from 'vue'
import NcAvatar from '@nextcloud/vue/components/NcAvatar'
import NcHeaderMenu from '@nextcloud/vue/components/NcHeaderMenu'
import AccountMenuEntry from '../components/AccountMenu/AccountMenuEntry.vue'
import AccountMenuProfileEntry from '../components/AccountMenu/AccountMenuProfileEntry.vue'
import logger from '../logger.js'

interface ISettingsNavigationEntry {
	/**
	 * id of the entry, used as HTML ID, for example, "settings"
	 */
	id: string
	/**
	 * Label of the entry, for example, "Personal Settings"
	 */
	name: string
	/**
	 * Icon of the entry, for example, "/apps/settings/img/personal.svg"
	 */
	icon: string
	/**
	 * Type of the entry
	 */
	type: 'settings' | 'link' | 'guest'
	/**
	 * Link of the entry, for example, "/settings/user"
	 */
	href: string
	/**
	 * Whether the entry is active
	 */
	active: boolean
	/**
	 * Order of the entry
	 */
	order: number
	/**
	 * Number of unread pf this items
	 */
	unread: number
	/**
	 * Classes for custom styling
	 */
	classes: string
}

// See: apps/user_status/src/services/statusOptionsService.js
// TODO: either import this again from the user_status app when core is migrated to Vue 3
// Or get rid of the forbidden import
const USER_DEFINABLE_STATUSES = [{
	type: 'online',
	label: t('user_status', 'Online'),
}, {
	type: 'away',
	label: t('user_status', 'Away'),
}, {
	type: 'busy',
	label: t('user_status', 'Busy'),
}, {
	type: 'dnd',
	label: t('user_status', 'Do not disturb'),
	subline: t('user_status', 'Mute all notifications'),
}, {
	type: 'invisible',
	label: t('user_status', 'Invisible'),
	subline: t('user_status', 'Appear offline'),
}]

export default defineComponent({
	name: 'AccountMenu',

	components: {
		AccountMenuEntry,
		AccountMenuProfileEntry,
		NcAvatar,
		NcHeaderMenu,
	},

	setup() {
		const settingsNavEntries = loadState<Record<string, ISettingsNavigationEntry>>('core', 'settingsNavEntries', {})
		const { profile: profileEntry, ...otherEntries } = settingsNavEntries

		return {
			currentDisplayName: getCurrentUser()?.displayName ?? getCurrentUser()!.uid,
			currentUserId: getCurrentUser()!.uid,

			profileEntry,
			otherEntries,

			t,
		}
	},

	data() {
		return {
			showUserStatus: false,
			userStatus: {
				status: null,
				icon: null,
				message: null,
			},
		}
	},

	computed: {
		translatedUserStatus() {
			return {
				...this.userStatus,
				status: this.translateStatus(this.userStatus.status),
			}
		},

		avatarDescription() {
			const description = [
				t('core', 'Avatar of {displayName}', { displayName: this.currentDisplayName }),
				...Object.values(this.translatedUserStatus).filter(Boolean),
			].join(' — ')
			return description
		},
	},

	async created() {
		if (!getCapabilities()?.user_status?.enabled) {
			return
		}

		const url = generateOcsUrl('/apps/user_status/api/v1/user_status')
		try {
			const response = await axios.get(url)
			const { status, icon, message } = response.data.ocs.data
			this.userStatus = { status, icon, message }
		} catch (error) {
			logger.error('Failed to load user status', { error })
		}
		this.showUserStatus = true
	},

	mounted() {
		subscribe('user_status:status.updated', this.handleUserStatusUpdated)
		emit('core:user-menu:mounted')
	},

	methods: {
		handleUserStatusUpdated(state) {
			if (this.currentUserId === state.userId) {
				this.userStatus = {
					status: state.status,
					icon: state.icon,
					message: state.message,
				}
			}
		},

		translateStatus(status) {
			const statusMap = Object.fromEntries(USER_DEFINABLE_STATUSES.map(({ type, label }) => [type, label]))
			if (statusMap[status]) {
				return statusMap[status]
			}
			return status
		},
	},
})
</script>

<style lang="scss" scoped>
:deep(#header-menu-user-menu) {
	padding: 0 !important;
}

.account-menu {
	:deep(*) {
		// do not apply the alpha mask on the avatar div
		mask: none !important;
	}

	&__avatar {
		--account-menu-outline: var(--border-width-input) solid color-mix(in srgb, var(--color-background-plain-text), transparent 75%);
		outline: var(--account-menu-outline);
		position: fixed;

		&:hover {
			--account-menu-outline: none;
			// Add hover styles similar to the focus-visible style
			border: var(--border-width-input-focused) solid var(--color-background-plain-text);
		}
	}

	&__list {
		display: inline-flex;
		flex-direction: column;
		padding-block: var(--default-grid-baseline) 0;
		padding-inline: 0 var(--default-grid-baseline);

		> :deep(li) {
			box-sizing: border-box;
			// basically "fit-content"
			flex: 0 1;
		}
	}

	// Ensure we do not waste space, as the header menu sets a default width of 350px
	:deep(.header-menu__content) {
		width: fit-content !important;
	}

	:deep(button) {
		// Normally header menus are slightly translucent when not active
		// this is generally ok but for the avatar this is weird so fix the opacity
		opacity: 1 !important;

		// The avatar is just the "icon" of the button
		// So we add the focus-visible manually
		&:focus-visible {
			.account-menu__avatar {
				--account-menu-outline: none;
				border: var(--border-width-input-focused) solid var(--color-background-plain-text);
			}
		}
	}
}
</style>
